<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>texture load</title>

</head>
<body>

<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;

attribute vec3 inPos;
attribute vec2 inUV;

varying vec3  vertPos;
varying vec2  vertUV;
    
uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
uniform mat4 u_textureMat44;

void main()
{   
    mat4 mv       = u_viewMat44 * u_modelMat44; 
    vertUV        = (u_textureMat44 * vec4(inUV, 0.0, 2.0)).st;
    vec4 viewPos  = mv * vec4( inPos, 1.0 );
    vertPos       = viewPos.xyz;
    gl_Position   = u_projectionMat44 * viewPos;
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;

varying vec3  vertPos;
varying vec2  vertUV;

uniform vec2      u_vp_size;
uniform sampler2D u_texture;

void main()
{
    vec4 texColor = texture2D(u_texture, vertUV.st);
    gl_FragColor  = vec4(texColor.rgb, 1.0);
} 
</script>

<style>
html,body { margin: 0; overflow: hidden; }
#gui { position : absolute; top : 0; left : 0; font-size : large; }
#ref-link { position : absolute; bottom : 0; left : 0; font-size : large; }  
</style>

<!--div id="ref-link">
<a href=""> 
</a>
</div-->

<canvas id="canvas" style="border: none;"></canvas>

<script type="text/javascript">

(function loadscene() {

let gl;

class App {

    constructor() {
        this.canvas = document.getElementById( "canvas");
        gl = canvas.getContext( "experimental-webgl" );
        //gl = canvas.getContext( "webgl2" );
        if ( !gl )
            return null;

        this.texture = new Texture( "https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/supermario.jpg" ); 
        this.texture.bound = false;

        this.progDraw = new ShProg( 
            [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
                { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
            ] );
        if ( !this.progDraw.progObj )
            return null;
        this.progDraw.inPos = this.progDraw.AttrI("inPos");
        this.progDraw.inUV  = this.progDraw.AttrI("inUV");
        this.progDraw.invalid = true;

        this.bufQuad = new VertexBuffer(
            [ { data : [ -1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0 ], attrSize : 3, attrLoc : this.progDraw.inPos },
            { data : [0, 0, 1, 0, 1, 1, 0, 1], attrSize : 2, attrLoc : this.progDraw.inUV } ],
            [ 0, 1, 2, 0, 2, 3 ] );

        this.resize(); 
        
        var self = this;
        window.onresize = function() { self.resize(); }
    }    

    resize() {
        //vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
        this.vp_size = [window.innerWidth, window.innerHeight]
        this.canvas.width = this.vp_size[0];
        this.canvas.height = this.vp_size[1];
    }

    draw(deltaMS){
        
        gl.viewport( 0, 0, this.vp_size[0], this.vp_size[1] );
        gl.enable( gl.DEPTH_TEST );
        gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
        gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );

        // set up draw shader
        if ( this.progDraw.invalid ) {
            this.progDraw.invalid = false;
            this.progDraw.Use();
            this.progDraw.SetM44("u_projectionMat44", Mat44.ident());
            this.progDraw.SetM44("u_viewMat44", Mat44.ident());
            this.progDraw.SetM44("u_modelMat44", Mat44.ident());
            this.progDraw.SetM44("u_textureMat44", Mat44.ident());
            this.progDraw.SetI1("u_texture", 0);
        }
        this.progDraw.SetF2("u_vp_size", this.vp_size);
        
        // bind the texture
        this.texture.bound = this.texture.bound || this.texture.bind( 0 );

        // draw scene
        this.bufQuad.Draw();
    }

}

class ShProg {
    constructor(shaderList) {
        let shaderObjs = [];
        for (let i_sh = 0; i_sh < shaderList.length; ++i_sh) {
            let shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
            if (shderObj) shaderObjs.push(shderObj);
        }
        this.progObj = this.Link(shaderObjs)
        if (this.progObj) {
            this.attrInx = {};
            var noOfAttributes = gl.getProgramParameter(this.progObj, gl.ACTIVE_ATTRIBUTES);
            for (var i_n = 0; i_n < noOfAttributes; ++i_n) {
                var name = gl.getActiveAttrib(this.progObj, i_n).name;
                this.attrInx[name] = gl.getAttribLocation(this.progObj, name);
            }
            this.uniLoc = {};
            var noOfUniforms = gl.getProgramParameter(this.progObj, gl.ACTIVE_UNIFORMS);
            for (var i_n = 0; i_n < noOfUniforms; ++i_n) {
                var name = gl.getActiveUniform(this.progObj, i_n).name;
                this.uniLoc[name] = gl.getUniformLocation(this.progObj, name);
            }
        }
    }

    AttrI(name) { return this.attrInx[name]; }
    UniformL(name) { return this.uniLoc[name]; }
    Use() { gl.useProgram(this.progObj); }
    SetI1(name, val) { if (this.uniLoc[name]) gl.uniform1i(this.uniLoc[name], val); }
    SetF1(name, val) { if (this.uniLoc[name]) gl.uniform1f(this.uniLoc[name], val); }
    SetF2(name, arr) { if (this.uniLoc[name]) gl.uniform2fv(this.uniLoc[name], arr); }
    SetF3(name, arr) { if (this.uniLoc[name]) gl.uniform3fv(this.uniLoc[name], arr); }
    SetF4(name, arr) { if (this.uniLoc[name]) gl.uniform4fv(this.uniLoc[name], arr); }
    SetM33(name, mat) { if (this.uniLoc[name]) gl.uniformMatrix3fv(this.uniLoc[name], false, mat); }
    SetM44(name, mat) { if (this.uniLoc[name]) gl.uniformMatrix4fv(this.uniLoc[name], false, mat); }
    
    Compile(source, shaderStage) {
        let shaderScript = document.getElementById(source);
        if (shaderScript)
            source = shaderScript.text;
        let shaderObj = gl.createShader(shaderStage);
        gl.shaderSource(shaderObj, source);
        gl.compileShader(shaderObj);
        let status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS);
        if (!status) alert(gl.getShaderInfoLog(shaderObj));
        return status ? shaderObj : null;
    }
    
    Link(shaderObjs) {
        let prog = gl.createProgram();
        for (let i_sh = 0; i_sh < shaderObjs.length; ++i_sh)
            gl.attachShader(prog, shaderObjs[i_sh]);
        gl.linkProgram(prog);
        status = gl.getProgramParameter(prog, gl.LINK_STATUS);
        if ( !status ) alert(gl.getProgramInfoLog(prog));
        return status ? prog : null;
    } 
}

class VertexBuffer {
    constructor(attribs, indices, type) {
        this.buf = [];
        this.attr = [];
        this.inxLen = indices.length;
        this.primitive_type = type ? type : gl.TRIANGLES;
        for (let i=0; i<attribs.length; ++i) {
            this.buf.push(gl.createBuffer());
            this.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
            gl.bindBuffer(gl.ARRAY_BUFFER, this.buf[i]);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
        }
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        if ( this.inxLen > 0 ) {
            this.inx = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.inx);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
        }
    }

    Draw() {
        for (let i=0; i<this.buf.length; ++i) {
            gl.bindBuffer(gl.ARRAY_BUFFER, this.buf[i]);
            gl.vertexAttribPointer(this.attr[i].loc, this.attr[i].size, gl.FLOAT, false, 0, 0);
            gl.enableVertexAttribArray(this.attr[i].loc);
        }
        if (this.inxLen > 0) {
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.inx);
            gl.drawElements(this.primitive_type, this.inxLen, gl.UNSIGNED_SHORT, 0);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
        }
        else
            gl.drawArrays(this.primitive_type, 0, this.attr[0].no_of);
        for (let i=0; i<this.buf.length; ++i)
            gl.disableVertexAttribArray(this.attr[i].loc);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
    }
}

class Texture {
    constructor( name, dflt ) {
        let texture = this;
        this.dflt = dflt || [128,128,128,255]
        let image = { "cx": this.dflt.w || 1, "cy": this.dflt.h || 1, "plane": this.dflt.p || this.dflt };
        this.size = [image.cx, image.cy];
        this.dummyObj = Texture.createTexture2D( image, true )
        this.image = new Image(64,64);
        this.image.setAttribute('crossorigin', 'anonymous');
        this.image.onload = function () {
            let cx = 1 << 31 - Math.clz32(texture.image.naturalWidth);
            if ( cx < texture.image.naturalWidth ) cx *= 2;
            let cy = 1 << 31 - Math.clz32(texture.image.naturalHeight);
            if ( cy < texture.image.naturalHeight ) cy *= 2;
            var canvas = document.createElement( 'canvas' );
            canvas.width  = cx;
            canvas.height = cy;
            var context = canvas.getContext( '2d' );
            context.drawImage( texture.image, 0, 0, canvas.width, canvas.height );
            texture.textureObj = Texture.createTexture2D( canvas, true );
            texture.size = [cx, cy];
        }
        this.image.src = name;
    }
    static createTexture2D( image, flipY ) {
        let t = gl.createTexture();
        gl.activeTexture( gl.TEXTURE0 );
        gl.bindTexture( gl.TEXTURE_2D, t );
        gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, flipY != undefined && flipY == true );
        if ( image.cx && image.cy && image.plane )
            gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, image.cx, image.cy, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(image.plane) );
        else
            gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT );
        gl.bindTexture( gl.TEXTURE_2D, null );
        return t;
    }
    bind( texUnit = 0 ) {
        gl.activeTexture( gl.TEXTURE0 + texUnit );
        if ( this.textureObj ) { 
            gl.bindTexture( gl.TEXTURE_2D, this.textureObj );
            return true;
        }
        gl.bindTexture( gl.TEXTURE_2D, this.dummyObj );
        return false;
    }
};

let Util = {
fract : function( val ) { 
return val - Math.trunc( val );
},
angle : function( deltaTime, intervall ) {
return this.fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
},
move : function( deltaTime, intervall, range ) {
var pos = this.fract( deltaTime / (1000*intervall) ) * 2.0
var pos = pos < 1.0 ? pos : (2.0-pos)
return range[0] + (range[1] - range[0]) * pos;
},    
ellipticalPosition : function( a, b, angRag ) {
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
}
}

Vec3 = {
cross: function( a, b ) { return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0 ]; },
dot: function( a, b ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; },
normalize: function( v ) {
var len = Math.sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] );
return [ v[0] / len, v[1] / len, v[2] / len ];
}
}

Mat44 = {
ident: function() { return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; },
rotateAxis: function(matA, angRad, axis) {
var aMap = [ [1, 2], [2, 0], [0, 1] ];
var a0 = aMap[axis][0], a1 = aMap[axis][1]; 
var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad);
var matB = matA.slice(0);
for ( var i = 0; i < 3; ++ i ) {
    matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng;
    matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng;
}
return matB;
},
rotate: function(matA, angRad, axis) {
    var s = Math.sin(-angRad), c = Math.cos(-angRad);
    var x = axis[0], y = axis[1], z = axis[2]; 
    matB = [
        x*x*(1-c)+c,   x*y*(1-c)-z*s, x*z*(1-c)+y*s, 0,
        y*x*(1-c)+z*s, y*y*(1-c)+c,   y*z*(1-c)-x*s, 0,
        z*x*(1-c)-y*s, z*y*(1-c)+x*s, z*z*(1-c)+c,   0,
        0,             0,             0,             1 ];
    return this.multiply(matA, matB);
},
multiply: function(matA, matB) {
matC = this.ident();
for (var i0=0; i0<4; ++i0 )
    for (var i1=0; i1<4; ++i1 )
        matC[i0*4+i1] = matB[i0*4+0] * matA[0*4+i1] + matB[i0*4+1] * matA[1*4+i1] + matB[i0*4+2] * matA[2*4+i1] + matB[i0*4+3] * matA[3*4+i1]  
return matC;
}
}

function render(deltaMS){
    app.draw(deltaMS);
    requestAnimationFrame(render);
}

let app = new App();
requestAnimationFrame(render);


})();
</script>

</body>
</html>